#include <render/texture2d.h>
#include <graphics/image2d.h>
#include <graphics/image3d.h>
#include <QtOpenGL>
#include <render/glextensions.h>
#include <assert.h>
#include <graphics/utils.h>


namespace yam3d
{
namespace render
{


Texture::Texture() : m_textureId(0)
{
    // Use tightly packed data
   // glPixelStorei ( GL_UNPACK_ALIGNMENT, 1 );
   // checkGlError("glPixelStorei(GL_UNPACK_ALIGNMENT, 1)");

    glGenTextures(1, &m_textureId);
    graphics::checkGlError("glGenTextures()");
}

Texture::~Texture()
{
    glDeleteTextures(1, &m_textureId);
    graphics::checkGlError("glDeleteTextures()");
}

Texture2D::Texture2D(graphics::Image2D* img, bool genMipMaps)
    : Texture()
{
    int format = img->getFormat() == 4 ? GL_RGBA : GL_RGB;

    glEnable(GL_TEXTURE_2D);
    graphics::checkGlError("glEnable(GL_TEXTURE_2D)");

    // Bind the texture object
    glBindTexture ( GL_TEXTURE_2D, m_textureId );
    graphics::checkGlError("glBindTexture(GL_TEXTURE_2D)");

    // Load the texture
    glTexImage2D ( GL_TEXTURE_2D, 0, format, img->width(), img->height(), 0, format, GL_UNSIGNED_BYTE, img->getPixel(0,0) );
    graphics::checkGlError("glTexImage2D(GL_TEXTURE_2D)");

    if( genMipMaps )
    {
        int level = 1;
        graphics::Image2D* prevImage = img;

        while ( prevImage->width() > 1 && prevImage->height() > 1 )
        {
           graphics::Image2D* newImage = prevImage->genMipMap2D();

           // Load the mipmap level
           glTexImage2D( GL_TEXTURE_2D, level, format, newImage->width(), newImage->height(), 0, format, GL_UNSIGNED_BYTE, newImage->getPixel(0,0) );
           graphics::checkGlError("glTexImage2D(GL_TEXTURE_2D)");

           // Delete prec img
           if( prevImage != img )
               delete prevImage;

           // Set the previous image for the next iteration
           prevImage = newImage;
           level++;

        }
    }

    glBindTexture ( GL_TEXTURE_2D, 0 );
    graphics::checkGlError("glBindTexture(GL_TEXTURE_2D, 0)");
}

Texture2D::~Texture2D()
{
}

void Texture2D::bind(int samplerUnit)
{
    assert( samplerUnit >= 0 && samplerUnit <= 2 );

    static const GLenum SAMPLER_UNIT_LOOKUP[] =
    {
        GL_TEXTURE0,
        GL_TEXTURE1,
        GL_TEXTURE2
    };

    assert(glActiveTexture != 0);
  //  glEnable(GL_TEXTURE_2D);
    glActiveTexture ( SAMPLER_UNIT_LOOKUP[samplerUnit] );
    graphics::checkGlError("glActiveTexture(%d)", samplerUnit);
    glBindTexture ( GL_TEXTURE_2D, m_textureId );
    graphics::checkGlError("glBindTexture(GL_TEXTURE_2D)");


 //   glEnable(GL_TEXTURE_2D);

    // Set the default wrapping and filtering modes
    glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_REPEAT);
    glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_REPEAT);
    glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
    glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
    graphics::checkGlError("glTexParameteri(GL_TEXTURE_2D)");
}



Texture3D::Texture3D(graphics::Image3D* img, bool genMipMaps)
    : Texture()
{
    glEnable(GL_TEXTURE_3D);
    glBindTexture(GL_TEXTURE_3D, m_textureId);

    glTexImage3D( GL_TEXTURE_3D, 0, GL_RGBA, img->width(), img->height(), img->depth(), 0, GL_RGBA, GL_UNSIGNED_BYTE, img->getPixel(0,0,0) );
    graphics::checkGlError("glTexImage3D(GL_TEXTURE_3D)");

    if( genMipMaps )
    {
        int level = 1;
        graphics::Image3D* prevImage = img;

        while ( prevImage->width() > 1 && prevImage->height() > 1 && prevImage->depth() > 1 )
        {
           graphics::Image3D* newImage = prevImage->genMipMap3D();

           // Load the mipmap level
           glTexImage3D( GL_TEXTURE_3D, 0, GL_RGBA, newImage->width(), newImage->height(), newImage->depth(), 0, GL_RGBA, GL_UNSIGNED_BYTE, newImage->getPixel(0,0,0) );

           // Delete prec img
           if( prevImage != img )
               delete prevImage;

           // Set the previous image for the next iteration
           prevImage = newImage;
           level++;
        }
    }
}

Texture3D::~Texture3D()
{
}

void Texture3D::bind(int samplerUnit)
{
    assert( samplerUnit >= 0 && samplerUnit <= 2 );

    static const GLenum SAMPLER_UNIT_LOOKUP[] =
    {
        GL_TEXTURE0,
        GL_TEXTURE1,
        GL_TEXTURE2
    };

    assert(glActiveTexture != 0);
    glActiveTexture ( SAMPLER_UNIT_LOOKUP[samplerUnit] );
    glBindTexture ( GL_TEXTURE_3D, m_textureId );
    glEnable(GL_TEXTURE_3D);

    // Set the default wrapping and filtering modes
    glTexParameteri(GL_TEXTURE_3D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
    glTexParameteri(GL_TEXTURE_3D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
    glTexParameteri(GL_TEXTURE_3D, GL_TEXTURE_WRAP_R, GL_CLAMP_TO_EDGE);
    glTexParameteri(GL_TEXTURE_3D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
    glTexParameteri(GL_TEXTURE_3D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
}

}
}
